/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2013/03/03
* File: VertexBuffer.h
* Blog: http://www.cnblogs.com/zengqh/
**/

#pragma once

#include "RenderObject.h"

namespace HY
{
class VertexBuffer : public RenderObject
{
public:
	enum 
	{
		Position     =  0,	/** Position data (1 channel) */
		BlendWeight  =  1,	/** Blend weight data (1 channel) */
		Normal       =  2,	/** Normal data (1 channel, only Float3/Half3 allowed due to restrictions of legacy APIs!) */
		Color        =  3,	/** Color data (2 channels, only RGBA allowed, second channel requires Capabilities::bVertexBufferSecondaryColor) */
		FogCoord     =  4,	/** Fog coordinate data (1 channel, only Float1 allowed, requires FixedFunctions::Capabilities::bVertexBufferFogCoord) */
		PointSize    =  5,	/** Point sprite size data (1 channel, only Float1 allowed! Known as "PSize", but this name may conflict with OS definitions) */
		BlendIndices =  6,	/** Blend indices data (1 channel) */
		TexCoord     =  7,	/** Texture coordinate data (n channels) */
		Tangent      =  8,	/** Tangent data (1 channel, only Float3/Half3 allowed due to restrictions of legacy APIs!) */
		Binormal     =  9	/** Binormal (also referred to as bitangent) data (1 channel, only Float3/Half3 allowed due to restrictions of legacy APIs!) */
	};

	static const uint8 NumOfSemantics      = 10;	/** Number of vertex attribute semantics */
	static const uint8 MaxPipelineChannels = 16;	/** Maximum possible number of channels */

	/**
	*  @brief
	*    Vertex attribute types
	*/
	enum 
	{
		Float1 = 0,	/** Float 1 (one component per element, 32 bit floating point per component) */
		Float2 = 1,	/** Float 2 (two components per element, 32 bit floating point per component) */
		Float3 = 2,	/** Float 3 (three components per element, 32 bit floating point per component) */
		Float4 = 3,	/** Float 4 (four components per element, 32 bit floating point per component) */
		Short2 = 4,	/** Short 2 (two components per element, 16 bit integer per component) */
		Short4 = 5,	/** Short 4 (four components per element, 16 bit integer per component) */
	};

public:
	struct Attribute 
	{
		uint32		semantic_;		/** Any member of the vertex attribute semantic enumeration type */
		uint32		channel_;		/** Pipeline channel (see uint32) */
		uint32		type_;			/** Any member of the uint32 enumeration type */
		uint32		offset_;		/** Offset (in bytes) of the vertex attribute */
		uint32		size_api_;		/** Size (in bytes) of the vertex attribute */
		uint32		type_api_;		/** API dependent vertex type */
		uint32		components_api;	/** Number of vertex type components */
	};

public:
	virtual ~VertexBuffer()
	{

	}

	virtual void bind() = 0;
	virtual void unbind() = 0;

	virtual bool create(uint32 elems, uint32 usage) = 0;

	virtual void* lock(uint32 flags) = 0;

	virtual bool unlock() = 0;

	uint32 getNumOfElements() const
	{
		return elems_;
	}

	uint32 getSize() const
	{
		return size_;
	}

	uint32 getUsage() const
	{
		return usage_;
	}

	uint32 getNumOfVertexAttributes() const
	{
		return attr_list_.size();
	}

	bool clearVertexAttributes()
	{
		attr_list_.clear();
		return true;
	}

	bool addVertexAttribute(uint32 semantic, uint32 channel, uint32 type)
	{
		/** some checking. */
		for (int32 i = 0; i < (int)attr_list_.size(); ++i)
		{
			if (attr_list_[i].semantic_ == semantic &&
				attr_list_[i].channel_ == channel)
			{
				/** Error, the vertex attribute is already defined! */
				HY_ASSERT(0);
				return false;
			}
		}

		/** Check normal, tangent and binormal 
			(only Float3/Half3 allowed due to restrictions of legacy APIs!)
		 */
		if ((semantic == Normal || semantic == Tangent || semantic == Binormal) &&
			type != Float3)
		{
			HY_ASSERT(0);
			return false;
		}

		if (semantic == Color)
		{
			HY_ASSERT(0);
			return false;
		}

		switch (semantic) 
		{
		case Position:
		case Normal:
			if (channel > 1)
			{
				HY_ASSERT(0);
				return false;
			}

			break;

		case Color:
			if (channel > 2)
			{
				HY_ASSERT(0);
				return false;
			}

			break;

		case BlendWeight:
		case FogCoord:
		case PointSize:
		case BlendIndices:
		case TexCoord:
		case Tangent:
		case Binormal:
		default:
			break;
		}

		Attribute attr;

		attr.semantic_ = semantic;
		attr.channel_ = channel;
		attr.type_ = type;
		attr.offset_ = attr_vertex_size;
		attr.size_api_ = 0;
		attr.type_api_ = 0;
		attr.components_api = 0;

		addVertexAttribute(attr);

		return true;
	}

	virtual bool addVertexAttribute(Attribute& attr) = 0;

	const Attribute* getVertexAttribute(uint32 semantic, uint32 channel) const
	{
		for (int i = 0; i < (int)attr_list_.size(); ++i)
		{
			const Attribute* attr = &attr_list_[i];
			if (attr->semantic_ == semantic && attr->channel_ == channel)
			{
				return attr;
			}
		}

		return 0;
	}

	const Attribute* getVertexAttribute(uint32 idx) const
	{
		if (idx >= attr_list_.size())
		{
			return 0;
		}

		return &attr_list_[idx];
	}

	uint32 getVertexSize() const
	{
		return attr_vertex_size;
	}

protected:
	VertexBuffer()
		: RenderObject(TypeVertexBuffer)
		, attr_vertex_size(0)
	{

	}

protected:
	uint32					attr_vertex_size;
	std::vector<Attribute>	attr_list_;
	uint32 					elems_;
	uint32 					size_;
	uint32 					usage_;
};
}